/*! scrollbar v1.0 ~ Sean Catchpole - Copyright 2010 MIT/GPL */
(function($,undefined){ if(!$) return;

  var old = $.scrollbar; //save settings
  $.scrollbar =
  { version: "1.0"
  // Settings
  , autorun: [ ".autoscrollbar" ]
  , installed: "scrollbarInstalled"
  , active: "scrollbarActive"
  , html:
    [ '<div class="scrollbox">'
    ,   '<div class="scrollarea">'
    ,     '<div class="scrollcontent">'
    ,       '$content'
    ,     '</div>'
    ,   '</div>'
    , '</div>'
    , '<div class="scrollbar">'
    ,   '<div class="arrow_up"></div>'
    ,   '<div class="bar">'
    ,     '<div class="top"></div>'
    ,     '<div class="bottom"></div>'
    ,     '<div class="middle">'
    ,       '<div class="slider">'
    ,         '<div class="top"></div>'
    ,         '<div class="bottom"></div>'
    ,         '<div class="middle"></div>'
    ,       '</div>'
    ,     '</div>'
    ,   '</div>'
    ,   '<div class="arrow_down"></div>'
    , '</div>'
    ].join('')
  , find:
    { box: '.scrollbox'
    , area: '.scrollarea'
    , content: '.scrollcontent'
    , scrollbar: '.scrollbar'
    , slider: '.slider'
    , bar: '.bar'
    , up: '.arrow_up'
    , down: '.arrow_down'
    }
  , delay: 200
  , repeatdelay: 50
  , scrollBy: 0
  , animate: 0
  , easing: "linear"
  // Core
  , uid: function(name){ return "" + name + (+new Date) }
  , load: function(elem){
      var s = elem.data("scrollbar") || {};
      return (s.load&&s.load!=this.load?s.load(elem):s)
    }
  , save: function(){ return this.elem.data("scrollbar",this) }
  , unload: function(elem){ return this.elem.removeData("scrollbar") }
  , args: function(args){
      $.extend.apply( this, [this].concat( [].slice.apply(args) ) );
      return (args[0]===true?"resize":(args[0]===false?"remove":"add"));
    }
  , create: function(html,obj){
      var s=this, id={};
      html = html.replace( /\$(\w+)/g, function(all,v){
        if( obj[v] != null ){
          id[v] = s.uid(v);
          return  '<div class="'+id[v]+'"></div>'
        } else { return '' }
      })
      html=$(html);
      $.each( id, function(v){
        html.find( '.' + id[v] ).replaceWith( obj[v] );
      });
      return html;
    }
  , add: function(){
      var s=this, content=$('<div>')
        , html = s.create( s.html, {content:content} );
      s.elems={}; s.sizes={};
      html=html.wrap('<div>').parent();
      $.each( s.find, function(i,f){ s.elems[i] = html.find( f ); });
      html=html.children();
      content.replaceWith( s.elem.children() );
      s.elems.box.css({ height:"100%", overflow:"hidden" });
      s.elems.area.css({ height:"100%", overflowY:"scroll" });
      s.elem.addClass( s.installed ).css({ overflow:"visible" }).append( html );
      s.listen();
      s.save();
      s.resize();
    }
  , remove: function(){
      var s=this, content;
      if( !s.elem.is('.'+s.installed) ){ return }
      content = s.elems.content.children();
      s.elem.before( content ).empty().append( content )
        .removeClass( s.installed ).css({ overflow:'' });
      s.unbind(s.elem,"resize");
      s.unload();
    }
  , saveSize: function(e){
      var s=this; s.sizes[e]=
      { width:s.elems[e].width(), height:s.elems[e].height() };
    }
  , resize: function(){
      var s=this, active, p;
      s.saveSize("box");
      s.saveSize("content");
      active = s.sizes.content.height > s.sizes.box.height;
      s.elem[(active?"add":"remove")+"Class"](s.active);
      if( active ){
        s.saveSize("bar");
        p = s.sizes.box.height / s.sizes.content.height;
        s.elems.slider.height(p * s.sizes.bar.height);
        s.saveSize("slider");
      }
      s.elems.area.width(s.sizes.box.width+s.width+1);
      s.update();
    }
  , update: function(){
      var s=this, p, barTop, sliderTop;
      if( !s.elem.is('.'+s.active) ){ return }
      p = s.elems.area.scrollTop()
          / (s.sizes.content.height -  s.sizes.box.height);
      if(p==s.percent){ return }
      else{ s.percent = p; }
      barTop = s.sizes.bar.height * p;
      sliderTop = s.sizes.slider.height * p;
      s.elems.slider.stop().animate({top:barTop-sliderTop},s.animate,s.easing);
    }
  , scrollTop:function(num){
      var s=this, rel, h = this.sizes.content.height;
      if(typeof num == "string"){
        num = $.trim(num);
        rel = /^[-+]/.exec(num);
        if (/%$/.test(num) ){
          num = (+num.replace(/%$/,'') || 0) / 100;
          num = (this.sizes.content.height - this.sizes.box.height) * num;
        }else{
          num = +num.replace(/px$/,'') || 0;
        }
        if(rel){ num = this.elems.area.scrollTop() + num; }
      }else{ num = +num; }
      num = (num<0?0:(num>h?h:num));
      this.elems.area.scrollTop(num);
      this.update();
      this.elem.trigger("scroll");
    }
  , evented: function(fn){ return function(e){ return fn.apply(e.data,arguments) } }
  , delayed: function(fn){
      //TODO: clean this function up
      var s=this;
      return function(e){
        var call = function(){ fn.call(s,e); }
          , timer = setTimeout(function(){
          call()
          var interval = setInterval(call,s.repeatdelay);
          $(document).one("mouseup",function(){ clearInterval(interval) });
        },s.delay);
        $(document).one("mouseup",function(){ clearTimeout(timer) });
        call();
      }
    }
  , scope: function(events){ return events.replace(/(\s+|$)/g, '.scrollbar$1') }
  , bind: function(elem,events,fn){
      elem.bind( this.scope(events), this, this.evented(fn) )
    }
  , unbind: function(elem,events){ elem.unbind( this.scope(events) ); }
  , mouse: function(e,elem){
      var o = elem.offset();
      return ( o ? {x:e.pageX-o.left, y:e.pageY-o.top} : {x:0, y:0} )
    }
  , clickBar: function(e){
      var m = this.mouse(e, this.elems.bar)
        , h = this.sizes.bar.height
        , t = (this.elems.slider.position() || {top:0}).top
        , sh = this.sizes.slider.height;
      if( m.y>=t && m.y <= t+sh ){ return false; }
      this.scrollTop( (m.y/h<this.percent?'-':'+') + this.sizes.box.height );
    }
  , scrollLine: function(num){
      return function(e){
        var line=""+(this.scrollBy || this.elems.area.css("line-height") || 0);
        line = line.replace(/\d+/,function(n){ return (+n)*num; });
        if( !/^[-+]/.test(line) ){ line = '+' + line; }
        this.scrollTop( line );
        e.preventDefault();
      }
    }
  , scrollWheel: function(e,delta){ this.scrollLine(-delta*3).call(this,e); }
  , startSlider: function(){
      this.bind( $(document), "mousemove", this.moveSlider );
      this.bind( $(document), "mouseup", this.stopSlider );
      return false;
    }
  , moveSlider: function(e){
      var m = this.mouse(e, this.elems.bar)
        , h = this.sizes.bar.height
        , p = m.y/h;
      this.scrollTop( (p<0?0:(p>1?1:p))*100 + '%' );
      //TODO: need to offset this by initial grab offset (which might be pixels)
    }
  , stopSlider: function(){ this.unbind( $(document), "mousemove mouseup" ) }
  , listen: function(){
      var s=this;
      s.bind( s.elems.area, "scroll", s.update );
      s.bind( s.elems.up    , "mousedown", s.delayed( s.scrollLine(-1) ));
      s.bind( s.elems.down  , "mousedown", s.delayed( s.scrollLine(+1) ));
      s.bind( s.elems.bar   , "mousedown", s.delayed( s.clickBar ) );
      s.bind( s.elems.slider, "mousedown", s.startSlider );
      s.bind( s.elem.add(s.elems.box,s.elems.scrollbar), "resize", s.resize );
      if(s.scrollBy !== false ){
        s.bind( s.elems.bar , "mousewheel", s.scrollWheel );
        s.bind( s.elems.area, "mousewheel", s.scrollWheel );
      }
    }
  , width: 0
  , height: 0
  , size: function(){
      var div = $("<div><div>scrollbarsize</div></div>")
          .css({ overflow:"scroll", width:300, height:300
               , position:"absolute", left:-9999, top:-9999 })
          .prependTo("body")
        , width = div.find("div").width()
        , height = div.find("div").height();
      div.remove();
      if(width){ this.width = 300 - width }
      if(height){ this.height = 300 - height }
    }
  , ready: function(){
      $.scrollbar.size();
      $.each( $.scrollbar.autorun, function(i,s){ $( s ).scrollbar() });
    }
  };

  $.fn.scrollbar = function(){
    var args=arguments;
    return this.each(function(){
      var elem=$(this), old=$.scrollbar.load(elem)
        , s = $.extend( {}, $.scrollbar, old )
        , action = s.args(args);
      if(old.remove && action!="resize"){ old.remove() }
      s.elem = elem;
      s[action]();
    });
  };

  if(old){ $.extend($.scrollbar,old) } //reload settings
  $($.scrollbar.ready); //bind startup function

})(jQuery);
